﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

namespace ToddMcDermid.SSIS
{
    #region CLASS: DTSComponentMetaData
    public abstract class DTSComponentMetaData
    {
        #region Private Variables
        private IDTSComponentMetaData100 _componentMetaData;
        #endregion

        #region Private Variables
        private bool _ioLoaded;
        private InputList _inputs;
        private OutputList _outputs;

        private bool _customPropertiesLoaded;
        private CustomPropertyList _customProperties;
        #endregion

        #region Constructor
        internal DTSComponentMetaData(IDTSComponentMetaData100 componentMetaData, InputList childInputList, OutputList childOutputList, CustomPropertyList childCustomPropertyList)
        {
            if (componentMetaData == null)
            {
                throw new ArgumentNullException("componentMetaData");
            }

            this._componentMetaData = componentMetaData;
            this._inputs = childInputList;
            this._outputs = childOutputList;
            this._customProperties = childCustomPropertyList;
        }
        #endregion

        #region Properties
        public virtual InputList Inputs
        {
            get
            {
                this.LoadIOFromMetaData();
                return this._inputs;
            }
        }

        public virtual OutputList Outputs
        {
            get
            {
                this.LoadIOFromMetaData();
                return this._outputs;
            }
        }

        protected virtual CustomPropertyList CustomProperties
        {
            get
            {
                this.LoadCustomPropertiesFromMetaData();
                return this._customProperties;
            }
        }
        #endregion

        #region Validate
        public virtual DTSValidationStatus Validate()
        {
            DTSValidationStatus status = DTSValidationStatus.VS_ISVALID;

            #region Validate I/O
            try
            {
                this.LoadIOFromMetaData();

                if (status == DTSValidationStatus.VS_ISVALID)
                {
                    status = this._inputs.Validate();
                }
                if (status == DTSValidationStatus.VS_ISVALID)
                {
                    status = this._outputs.Validate();
                }
            }
            #region catch ...
            catch
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
            }
            #endregion
            #endregion
            #region Validate Custom Properties
            try
            {
                this.LoadCustomPropertiesFromMetaData();
                if (status == DTSValidationStatus.VS_ISVALID)
                {
                    status = this._customProperties.Validate();
                }
            }
            #region catch ...
            catch
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
            }
            #endregion
            #endregion
            return status;
        }
        #endregion

        #region ReinitializeMetaData
        public virtual void ReinitializeMetaData()
        {
            #region Fix up I/O
            try
            {
                this.LoadIOFromMetaData();
                this._inputs.ReinitializeMetaData();
                this._outputs.ReinitializeMetaData();
            }
            #region catch ...
            catch
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
            }
            #endregion
            #endregion

            #region Fix up Custom Properties
            try
            {
                this.LoadCustomPropertiesFromMetaData();
                this._customProperties.ReinitializeMetaData();
            }
            #region catch ...
            catch
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
            }
            #endregion
            #endregion
        }
        #endregion

        #region Simpler Events
        public void FireError(string errorMessage)
        {
            bool pbCancel = false;
            this._componentMetaData.FireError(0, this._componentMetaData.Name, errorMessage, "", 0, out pbCancel);
        }

        public void FireWarning(string warningMessage)
        {
            this._componentMetaData.FireWarning(0, this._componentMetaData.Name, warningMessage, "", 0);
        }
        #endregion

        #region LoadIOFromMetaData
        private void LoadIOFromMetaData()
        {
            if (!this._ioLoaded)
            {
                try
                {
                    #region Retrieve inputs and outputs
                    this._inputs.Clear();
                    foreach (IDTSInput100 input in this._componentMetaData.InputCollection)
                    {
                        this._inputs.Add(input);
                    }

                    this._outputs.Clear();
                    foreach (IDTSOutput100 output in this._componentMetaData.OutputCollection)
                    {
                        this._outputs.Add(output);
                    }
                    #endregion
                }
                #region catch ...
                catch
                {
                    if (System.Diagnostics.Debugger.IsAttached)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                }
                #endregion

                this._ioLoaded = true;
            }
        }
        #endregion

        #region LoadPropertiesFromMetaData
        private void LoadCustomPropertiesFromMetaData()
        {
            if (!this._customPropertiesLoaded)
            {
                try
                {
                    #region Retrieve Properties
                    this._customProperties.Clear();
                    foreach (IDTSCustomProperty100 customProperty in this._componentMetaData.CustomPropertyCollection)
                    {
                        this._customProperties.Add(customProperty);
                    }
                    #endregion
                }
                #region catch ...
                catch
                {
                    if (System.Diagnostics.Debugger.IsAttached)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                }
                #endregion

                this._customPropertiesLoaded = true;
            }
        }
        #endregion

        #region CLASS: InputList
        // TODO - remove inheritance of List
        public abstract class InputList : List<IDTSInput100>
        {
            #region Private Variables
            private IDTSComponentMetaData100 _componentMetaData;
            #endregion

            #region Constructor
            public InputList(IDTSComponentMetaData100 componentMetaData)
            {
                this._componentMetaData = componentMetaData;
            }
            #endregion

            #region Indexer by name
            public IDTSInput100 this[string name]
            {
                get
                {
                    foreach (IDTSInput100 input in this)
                    {
                        if (input.Name == name)
                        {
                            return input;
                        }
                    }
                    return null;
                }
            }
            #endregion

            #region Contains
            public bool Contains(string name)
            {
                foreach (IDTSInput100 input in this)
                {
                    if (input.Name == name)
                    {
                        return true;
                    }
                }

                return false;
            }
            #endregion

            #region Add
            public IDTSInput100 Add(string name)
            {
                if (!this.Contains(name))
                {
                    IDTSInput100 input = this._componentMetaData.InputCollection.New();
                    input.Name = name;
                    base.Add(input);
                    return input;
                }
                return null;
            }
            #endregion

            #region ExpectedInputNames
            public abstract string[] ExpectedInputNames { get; }
            #endregion

            #region Validate
            /// <summary>Validates the inputs - specifically:
            /// That the expected number of inputs exist, and are
            /// named as expected.</summary>
            /// <returns></returns>
            public virtual DTSValidationStatus Validate()
            {
                #region Check the input count
                if (this._componentMetaData.InputCollection.Count != this.ExpectedInputNames.Length)
                {
                    this.FireError("The component expected " + this.ExpectedInputNames.Length.ToString() + " inputs, "
                        + "but " + this._componentMetaData.InputCollection.Count.ToString() + " have been defined.");
                    return DTSValidationStatus.VS_NEEDSNEWMETADATA;
                }
                #endregion

                #region Check the expected input exists
                foreach (string expectedInputName in this.ExpectedInputNames)
                {
                    if (this[expectedInputName] == null)
                    {
                        this.FireError("The expected input '" + expectedInputName + "' does not exist.");
                        return DTSValidationStatus.VS_NEEDSNEWMETADATA;
                    }
                }
                #endregion

                return DTSValidationStatus.VS_ISVALID;
            }
            #endregion

            #region ReinitializeMetaData
            public virtual void ReinitializeMetaData()
            {
                #region Remove unused inputs
                List<int> unusedInputIDs = new List<int>();
                foreach (IDTSInput100 input in this)
                {
                    bool expected = false;
                    foreach (string expectedName in this.ExpectedInputNames)
                    {
                        if (input.Name == expectedName)
                        {
                            expected = true;
                            break;
                        }
                    }
                    if (!expected)
                    {
                        unusedInputIDs.Add(input.ID);
                    }
                }
                foreach (int unusedInputID in unusedInputIDs)
                {
                    this._componentMetaData.InputCollection.RemoveObjectByID(unusedInputID);
                }
                #endregion

                #region Add expected inputs
                foreach (string expectedName in this.ExpectedInputNames)
                {
                    bool found = false;
                    foreach (IDTSInput100 input in this)
                    {
                        if (input.Name == expectedName)
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        this.Add(expectedName);
                    }
                }
                #endregion

                #region Remove input columns that no longer exist on the virtual input
                try
                {
                    if (!this._componentMetaData.AreInputColumnsValid)
                    {
                        this._componentMetaData.RemoveInvalidInputColumns();
                    }
                }
                #region catch ...
                catch
                {
                    if (System.Diagnostics.Debugger.IsAttached)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                }
                #endregion
                #endregion
            }
            #endregion

            #region Simpler Events
            public void FireError(string errorMessage)
            {
                bool pbCancel = false;
                this._componentMetaData.FireError(0, this._componentMetaData.Name, errorMessage, "", 0, out pbCancel);
            }

            public void FireWarning(string warningMessage)
            {
                this._componentMetaData.FireWarning(0, this._componentMetaData.Name, warningMessage, "", 0);
            }
            #endregion

        }
        #endregion

        #region CLASS: OutputList
        // TODO - remove inheritance of List
        public abstract class OutputList : List<IDTSOutput100>
        {
            #region Private Variables
            private IDTSComponentMetaData100 _componentMetaData;
            #endregion

            #region Constructor
            public OutputList(IDTSComponentMetaData100 componentMetaData)
            {
                this._componentMetaData = componentMetaData;
            }
            #endregion

            #region Indexer by name
            public IDTSOutput100 this[string name]
            {
                get
                {
                    foreach (IDTSOutput100 output in this)
                    {
                        if (output.Name == name)
                        {
                            return output;
                        }
                    }
                    return null;
                }
            }
            #endregion

            #region Contains
            public bool Contains(string name)
            {
                foreach (IDTSOutput100 output in this)
                {
                    if (output.Name == name)
                    {
                        return true;
                    }
                }

                return false;
            }
            #endregion

            #region Add
            public IDTSOutput100 Add(string name)
            {
                if (!this.Contains(name))
                {
                    IDTSOutput100 output = this._componentMetaData.OutputCollection.New();
                    output.Name = name;
                    base.Add(output);
                    return output;
                }
                return null;
            }
            #endregion

            #region ExpectedOutputNames
            public abstract string[] ExpectedOutputNames { get; }
            #endregion

            #region Validate
            public virtual DTSValidationStatus Validate()
            {
                #region Check the output count
                if (this._componentMetaData.OutputCollection.Count != this.ExpectedOutputNames.Length)
                {
                    this.FireError("The component expected " + this.ExpectedOutputNames.Length.ToString() + " outputs, "
                        + "but " + this._componentMetaData.OutputCollection.Count.ToString() + " have been defined.");
                    return DTSValidationStatus.VS_NEEDSNEWMETADATA;
                }
                #endregion

                #region Check the expected outputs exist
                foreach (string expectedOutputName in this.ExpectedOutputNames)
                {
                    if (this[expectedOutputName] == null)
                    {
                        this.FireError("The expected output '" + expectedOutputName + "' does not exist.");
                        return DTSValidationStatus.VS_NEEDSNEWMETADATA;
                    }
                }
                #endregion

                return DTSValidationStatus.VS_ISVALID;
            }
            #endregion

            #region ReinitializeMetaData
            public virtual void ReinitializeMetaData()
            {
                #region Remove unused outputs
                List<int> unusedOutputIDs = new List<int>();
                foreach (IDTSOutput100 output in this)
                {
                    bool expected = false;
                    foreach (string expectedName in this.ExpectedOutputNames)
                    {
                        if (output.Name == expectedName)
                        {
                            expected = true;
                            break;
                        }
                    }
                    if (!expected)
                    {
                        unusedOutputIDs.Add(output.ID);
                    }
                }
                foreach (int unusedOutputID in unusedOutputIDs)
                {
                    this._componentMetaData.OutputCollection.RemoveObjectByID(unusedOutputID);
                }
                #endregion

                #region Add expected inputs
                foreach (string expectedName in this.ExpectedOutputNames)
                {
                    bool found = false;
                    foreach (IDTSOutput100 output in this)
                    {
                        if (output.Name == expectedName)
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        this.Add(expectedName);
                    }
                }
                #endregion
            }
            #endregion

            #region Simpler Events
            public void FireError(string errorMessage)
            {
                bool pbCancel = false;
                this._componentMetaData.FireError(0, this._componentMetaData.Name, errorMessage, "", 0, out pbCancel);
            }

            public void FireWarning(string warningMessage)
            {
                this._componentMetaData.FireWarning(0, this._componentMetaData.Name, warningMessage, "", 0);
            }
            #endregion
        }
        #endregion

        #region CLASS: CustomPropertyList
        // TODO - remove inheritance on List
        public abstract class CustomPropertyList : List<IDTSCustomProperty100>
        {
            #region Private Constants
            private const string USERCOMPONENTTYPENAME = "UserComponentTypeName";
            #endregion

            #region Private Variables
            private IDTSComponentMetaData100 _componentMetaData;
            #endregion

            #region Constructor
            public CustomPropertyList(IDTSComponentMetaData100 componentMetaData)
            {
                this._componentMetaData = componentMetaData;
            }
            #endregion

            #region Indexer by name
            public IDTSCustomProperty100 this[string name]
            {
                get
                {
                    foreach (IDTSCustomProperty100 customProperty in this)
                    {
                        if (customProperty.Name == name)
                        {
                            return customProperty;
                        }
                    }
                    return null;
                }
            }
            #endregion

            #region Contains
            public bool Contains(string name)
            {
                foreach (IDTSCustomProperty100 customProperty in this)
                {
                    if (customProperty.Name == name)
                    {
                        return true;
                    }
                }

                return false;
            }
            #endregion

            #region Add
            public IDTSCustomProperty100 Add(string name)
            {
                if (!this.Contains(name))
                {
                    IDTSCustomProperty100 customProperty = this._componentMetaData.CustomPropertyCollection.New();
                    customProperty.Name = name;
                    base.Add(customProperty);
                    return customProperty;
                }
                return null;
            }
            #endregion

            #region Validate
            public virtual DTSValidationStatus Validate()
            {
                return DTSValidationStatus.VS_ISVALID;
            }
            #endregion

            #region ReinitializeMetaData
            public abstract string[] ExpectedCustomPropertyNames { get; }

            public virtual void ReinitializeMetaData()
            {
                List<int> unusedCustomPropertyIDs = new List<int>();
                foreach (IDTSCustomProperty100 customProperty in this)
                {
                    if (customProperty.Name != USERCOMPONENTTYPENAME)
                    {
                        bool expected = false;
                        foreach (string expectedName in this.ExpectedCustomPropertyNames)
                        {
                            if (customProperty.Name == expectedName)
                            {
                                expected = true;
                                break;
                            }
                        }
                        if (!expected)
                        {
                            unusedCustomPropertyIDs.Add(customProperty.ID);
                        }
                    }
                }
                foreach (int unusedCustomPropertyID in unusedCustomPropertyIDs)
                {
                    this._componentMetaData.CustomPropertyCollection.RemoveObjectByID(unusedCustomPropertyID);
                }

                this.SaveToMetaData();
            }
            #endregion

            #region SaveToMetaData
            public abstract void SaveToMetaData();
            #endregion

            #region Simpler Events
            public void FireError(string errorMessage)
            {
                bool pbCancel = false;
                this._componentMetaData.FireError(0, this._componentMetaData.Name, errorMessage, "", 0, out pbCancel);
            }

            public void FireWarning(string warningMessage)
            {
                this._componentMetaData.FireWarning(0, this._componentMetaData.Name, warningMessage, "", 0);
            }
            #endregion
        }
        #endregion
    }

    #endregion
}
